package com.lecoboy.designpatterns;

import com.lecoboy.designpatterns.factory.SendFactory;
import com.lecoboy.designpatterns.factory.SendFactory2;
import com.lecoboy.designpatterns.factory.SendFactory3;
import com.lecoboy.designpatterns.factory.Sender;
import com.lecoboy.designpatterns.factory.abstractFactory.Provider;
import com.lecoboy.designpatterns.factory.abstractFactory.SendMailFactory;
import com.lecoboy.designpatterns.factory.abstractFactory.SendSmsFactory;
import org.junit.Test;

/**
 * 工厂方法模式 测试类
 */
public class FactoryTest {
    /**
     * TODO 普通工厂模式
     * 就是建立一个工厂类，对实现了同一接口的一些类进行实例的创建
     */
    @Test
    public void SendFactoryTest() {
        SendFactory sendFactory = new SendFactory();
        //mail
        Sender sender = sendFactory.produce(SendFactory.SenderType.EMAIl);
        sender.Send();
        //sms
        Sender senderSms = sendFactory.produce(SendFactory.SenderType.SMS);
        senderSms.Send();
    }

    /**
     * TODO 多个工厂方法模式
     * 是对普通工厂方法模式的改进，在普通工厂方法模式中，如果传递的字符串出错，则不能正确创建对象，而多个工厂方法模式是提供多个工厂方法，分别创建对象
     */
    @Test
    public void SendFactory2Test() {
        SendFactory2 sendFactory2 = new SendFactory2();
        //mail
        Sender sender = sendFactory2.produceSms();
        sender.Send();
        //sms
        sender = sendFactory2.produceMail();
        sender.Send();
    }

    /**
     * TODO 静态工厂方法模式
     * 将上面的多个工厂方法模式里的方法置为静态的，不需要创建实例，直接调用即可
     * 总体来说，工厂模式适合：凡是出现了大量的产品需要创建，并且具有共同的接口时，可以通过工厂方法模式进行创建。
     * 在以上的三种模式中，第一种如果传入的字符串有误，
     * 不能正确创建对象，第三种相对于第二种，不需要实例化工厂类，
     * 所以，大多数情况下，我们会选用第三种——静态工厂方法模式。
     */
    @Test
    public void SendFactory3Test() {
        //mail
        Sender sender = SendFactory3.produceMail();
        sender.Send();
        //sms
        sender = SendFactory3.produceSms();
        sender.Send();
    }

    /**
     * TODO 抽象工厂
     * 工厂方法模式有一个问题就是，类的创建依赖工厂类，也就是说，如果想要拓展程序，必须对工厂类进行修改，这违背了闭包原则，
     * 所以，从设计角度考虑，有一定的问题，如何解决？就用到抽象工厂模式，创建多个工厂类，这样一旦需要增加新的功能，直接增加新的工厂类就可以了，不需要修改之前的代码。
     * 其实这个模式的好处就是，如果你现在想增加一个功能：
     * 发及时信息，则只需做一个实现类，实现Sender接口，同时做一个工厂类，实现Provider接口，就OK了，无需去改动现成的代码。这样做，拓展性较好！
     */
    @Test
    public void SendAbstractFactoryTest() {
        //mail
        Provider provider = new SendMailFactory();
        Sender sender = provider.produce();
        sender.Send();
        //sms
        Provider providerSms = new SendSmsFactory();
        Sender senderSms = providerSms.produce();
        senderSms.Send();
    }
}
